/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.colswedev.lab4.dao;

import org.colswedev.lab4.dao.exception.UniqueConstraintException;
import org.colswedev.lab4.dao.exception.UncompleteInformationException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.colswedev.lab4.entity.Sistema;
import org.colswedev.lab4.entity.anotacion.NotNull;
import org.colswedev.lab4.entity.anotacion.Unique;

/**
 *
 * @author miguel
 */
public class GeneralDao {

    public <T> T getElement(Integer id, Class<T> returnType) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        final Sistema sistema = Sistema.getInstance();
        T ret = null;
        for (final T t : (List<T>) sistema.getClass().getMethod("get" + returnType.getSimpleName() + "s").invoke(sistema)) {
            if (((Integer) t.getClass().getMethod("getId").invoke(t)).intValue() == id.intValue()) {
                ret = t;
                break;
            }
        }
        return ret;
    }

    public <T> boolean deleteElement(Integer id, Class<T> clazz) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        boolean ret = false;
        final Sistema sistema = Sistema.getInstance();
        List<T> list = (List<T>) sistema.getClass().getMethod("get" + clazz.getSimpleName() + "s").invoke(sistema);

        //Problema a solucionar: cuando elimina el for-each presenta problema
//        for (final T t : list) {
//            if (((Integer) t.getClass().getMethod("getId").invoke(t)).intValue() == id.intValue()) {
//                list.remove(t); 
//                ret = true;
//            }
//        }

//        Primera Solucion
        for (int i = 0; i < list.size(); i++) {
            final T t= list.get(i);
            if (((Integer) t.getClass().getMethod("getId").invoke(t)).intValue() == id.intValue()) {
                list.remove(i--); //Ya se le indica cual debe borrar.
                ret = true;
            }

        }

        //Otra solucion (menos eficiente)

//        for (final T t : new ArrayList<T>(list)) {
//            if (((Integer) t.getClass().getMethod("getId").invoke(t)).intValue() == id.intValue()) {
//                list.remove(t); //le toca buscar el objeto en la lista
//                ret = true;
//            }
//        }
        return ret;
    }

    public <T> void addElement(T toAdd) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        final Sistema sistema = Sistema.getInstance();
        List<?> elements = null;
        for (Field f : toAdd.getClass().getDeclaredFields()){
            if (f.isAnnotationPresent(NotNull.class) && toAdd.getClass().getMethod("get"+f.getName().substring(0,1).toUpperCase()+f.getName().substring(1)).invoke(toAdd)==null){
                throw new UncompleteInformationException();
            }
            if (f.isAnnotationPresent(Unique.class)){
                Object v = toAdd.getClass().getMethod("get"+f.getName().substring(0,1).toUpperCase()+f.getName().substring(1)).invoke(toAdd);
                if (elements == null){
                    elements = findAll(toAdd.getClass());
                }
                for (Object o : elements){
                    if (o.getClass().getMethod("get"+f.getName().substring(0,1).toUpperCase()+f.getName().substring(1)).invoke(o).equals(v)){
                        throw new UniqueConstraintException();
                    }
                }
            }
        }
        ((List<T>) sistema.getClass().getMethod("get" + toAdd.getClass().getSimpleName() + "s").invoke(sistema)).add(toAdd);
    }

    public <T> List<T> findAll(Class<T> returnType) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        final Sistema sistema = Sistema.getInstance();
        return (List<T>) sistema.getClass().getMethod("get" + returnType.getSimpleName() + "s").invoke(sistema);
    }
}
